<template>
  <div class="upload-wrap">
    <el-upload
      ref="upload"
      action="#"
      list-type="picture-card"
      accept="image/jpg, image/jpeg, image/png, image/bmp"
      :limit="limit"
      :multiple="false"
      :auto-upload="false"
      :show-file-list="false"
      :disabled="disabled"
      :on-change="changeHandler"
      :http-request="upload"
    >
      <div v-for="(item, index) in imgUrlArr" :key="index" class="el-upload-list__item" @click.stop>
        <div v-if="isEdit">
          <img :class="{'img':isEdit,'img_isEdit':!isEdit}" :src="item.url || item" alt />
          <h5 v-if="!!titles[index]" class="title">{{ titles[index] }}</h5>
          <span class="el-upload-list__item-actions">
            <span class="el-upload-list__item-dot">
              <i class="el-icon-zoom-in" @click="handlePreview(index)" />
            </span>
            <span class="el-upload-list__item-dot" v-if="!disabled">
              <i class="el-icon-delete" @click="handleRemove(index)" />
            </span>
            <span class="el-upload-list__item-dot">
              <i class="el-icon-download" @click="downloadHandle(index)" />
            </span>
          </span>
        </div>
        <div v-else>
          <img :class="{'img':isEdit,'img_isEdit':!isEdit}" :src="item.url || item" alt />
        </div>
      </div>

      <div
        v-if="imgUrlArr.length !== limit&&!disabled"
        slot="default"
        class="upload-icon-plus el-upload-list__item"
      >
        <i class="el-icon-plus" />
        <span>{{ titles[imgUrlArr.length] }}</span>
      </div>
      <div slot="tip" class="el-upload__tip">{{ finalTipText }}</div>
    </el-upload>
    <!-- 图片预览弹窗 -->
    <BaseDialog :visible.sync="dialogVisible" title="图片预览">
      <div class="btn_yl">
        <div class="preview-wrap" ref="rotate">
          <!-- <img :src="dialogImageUrl" alt /> -->
          <el-image :src="dialogImageUrl"></el-image>
        </div>
        <div class="btn_bottom">
          <i class="el-icon-refresh-left btn_ys" @click="handleXzright">90</i>
          <i class="el-icon-refresh-right btn_ys" @click="handleXzleft">90</i>
        </div>
      </div>
    </BaseDialog>
    <!-- 剪裁组件弹窗 -->
    <BaseDialog
      :visible.sync="cropperModel"
      title="图片裁剪"
      width="760px"
      :containerStyle="containerStyle"
      @close="beforeClose"
    >
      <CropperPanel
        ref="uploadCropper"
        :cropperOption="cropperOption"
        :img-file="file"
        :fixed-number="fixedSize"
        :loading.sync="isLoading"
        @upload="uploadHandler"
      />
    </BaseDialog>
  </div>
</template>

<script>
/**
 * @Author: 焦质晔
 * @Date: 2019-06-20 10:00:00
 * @Last Modified by:   焦质晔
 * @Last Modified time: 2019-06-20 10:00:00
 **/
import _ from 'lodash';
import axios from '@/api/fetch';
import canvasCompress from './compress';
import CropperPanel from './CropperPanel';
export default {
  name: 'UploadCropper',
  components: {
    CropperPanel
  },
  props: {
    isEdit: {
      //     是否编辑  默认为true  刚加的
      type: Boolean,
      default: true
    },
    actionUrl: {
      type: String,
      required: true
    },
    initialValue: {
      type: Array,
      default: () => []
    },
    isCalcHeight: {
      type: Boolean,
      default: false
    },
    fixedSize: {
      type: Array,
      default: () => [5, 4]
    },
    titles: {
      type: Array,
      default: () => []
    },
    limit: {
      type: Number,
      default: 1
    },
    fileTypes: {
      type: Array,
      default: () => ['jpg', 'png', 'bmp']
    },
    disabled: {
      type: Boolean,
      default: false
    },
    tipText: {
      type: String
    },
    isFileName: {
      type: Boolean,
      default: false
    },
    cropperOption: {
      type: Object,
      default: () => {}
    },
    isCompress: {
      type: Boolean,
      default: true
    }
  },
  data() {
    this.uploadWrap = null;
    this.fileData = null; // 文件裁剪之后的 blob
    this.uid = ''; // 文件的 uid
    this.width = 148;
    this.height = 148;
    this.dialogImageUrl = ''; // 预览图片地址
    return {
      deg: 0,
      file: null, // 当前被选择的图片文件
      imgUrlArr: this.initialValue,
      dialogVisible: false,
      cropperModel: false,
      isLoading: false,
      uploadType: 'source', // 默认是 source, { source: 原图上传, cropper: 剪裁上传 }
      containerStyle: { paddingBottom: '10px' }
    };
  },
  computed: {
    finalTipText() {
      return _.isNil(this.tipText) ? `只能上传 ${this.fileTypes.join(',')} 格式的图片` : this.tipText;
    }
  },
  watch: {
    initialValue(val) {
      this.imgUrlArr = val;
      !val.length && this.clearFiles();
    },
    imgUrlArr(val) {
      this.$emit('success', val);
      // 取消表单校验
      if (val.length === this.limit && this.$parent.clearValidate) {
        this.$parent.clearValidate();
      }
    }
  },
  mounted() {
    this.uploadWrap = this.$refs.upload.$el.querySelector('.el-upload');
    this.setUploadWrapHeight();
  },
  updated() {
    const { uploadCropper } = this.$refs;
    uploadCropper && uploadCropper.Update();
  },
  methods: {
    handleXzright() {
      // 右旋转
      this.deg -= 90;
      if (this.deg <= -360) {
        this.deg = 0;
      }
      this.$refs.rotate.style.transform = `rotate(${this.deg}deg)`;
    },
    handleXzleft() {
      // 左旋转
      this.deg += 90;
      if (this.deg >= 360) {
        this.deg = 0;
      }
      this.$refs.rotate.style.transform = `rotate(${this.deg}deg)`;
    },
    handlePreview(index) {
      this.dialogImageUrl = this.imgUrlArr[index].url || this.imgUrlArr[index];
      this.dialogVisible = true;
    },
    handleRemove(index) {
      this.imgUrlArr.splice(index, 1);
    },
    clearFiles() {
      this.$refs.upload.clearFiles();
    },
    changeHandler(file, files) {
      //  图片点击上传之后的方法
      let fs = this.fileTypes.join(',');
      fs = fs.includes('jpg') ? (fs += ',jpeg').split(',') : fs.split(',');
      if (!fs.includes(file.raw.type.split('/')[1])) {
        //  原代码 this.uid === file.uid || !fs.includes(file.raw.type.split('/')[1])
        this.$message.error(`请上传！${this.fileTypes.join('，')}格式的图片`);
        this.beforeClose();
        return false;
      }
      this.uid = file.uid;
      this.file = file;
      this.fileData = file; // 默认是【原图上传】
      if (this.isEdit) {
        this.cropperModel = true;
      } else {
        this.getBase64(file.raw).then(res => {
          this.fileData = res;
          this.$refs.upload.submit();
        });
      }
    },
    uploadHandler(data, btnType) {
      if (btnType !== 'source') {
        this.fileData = data;
      }
      this.uploadType = btnType || 'cropper';
      this.$refs.upload.submit();
    },
    async upload(params) {
      const formData = new FormData();
      let file = params.file;
      if (this.uploadType !== 'source') {
        if (this.isCompress) {
          const base64 = await canvasCompress({
            img: this.fileData,
            type: 'jpg',
            fillColor: '#fff',
            width: 750
          });
          file = this.dataURItoBlob(base64.img);
        } else {
          file = this.dataURItoBlob(this.fileData);
        }
      }
      // 有的后台需要传文件名，不然会报错
      formData.append('file', file, this.file.name);
      try {
        // const res = await axios.post(this.actionUrl, formData);
        const res = await axios.post(this.actionUrl, formData); // 统一文件上传地址
        if (res.resultCode === 200) {
          this.imgUrlArr.push(this.isFileName ? { name: this.file.name, url: res.data } : res.data);
        }
      } catch (err) {
        this.clearFiles();
        this.$emit('error', err);
        this.$message.error('图片上传失败！');
      }
      this.cropperModel = false;
      this.isLoading = false;
    },
    beforeClose() {
      this.clearFiles();
      this.cropperModel = false;
      this.isLoading = false;
    },
    setUploadWrapHeight() {
      const iHeight = !this.isCalcHeight ? this.height : Number.parseInt((this.width * this.fixedSize[1]) / this.fixedSize[0]);
      this.uploadWrap.style.height = `${iHeight}px`;
      this.uploadWrap.style.lineHeight = `${iHeight - 2}px`;
    },
    // base64 转成 bolb 对象
    dataURItoBlob(base64Data) {
      let byteString;
      if (base64Data.split(',')[0].indexOf('base64') >= 0) {
        byteString = atob(base64Data.split(',')[1]);
      } else {
        byteString = unescape(base64Data.split(',')[1]);
      }
      let mimeString = base64Data
        .split(',')[0]
        .split(':')[1]
        .split(';')[0];
      let ia = new Uint8Array(byteString.length);
      for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }
      return new Blob([ia], { type: mimeString });
    },
    async downloadHandle(index) {
      try {
        Object.prototype.toString.call(this.imgUrlArr[index]) === '[object Object]' ? await this.downloadFile(this.imgUrlArr[index]) : window.open(this.imgUrlArr[index]);
      } catch (err) {
        this.$message.error('图片下载失败！');
      }
    },
    //  blob图片转base64
    getBase64(file) {
      return new Promise(function(resolve, reject) {
        let reader = new FileReader();
        let imgResult = '';
        reader.readAsDataURL(file);
        reader.onload = function() {
          imgResult = reader.result;
        };
        reader.onerror = function(error) {
          reject(error);
        };
        reader.onloadend = function() {
          resolve(imgResult);
        };
      });
    },
    // 获取服务端文件 to blob
    async downLoadByUrl(url, params = {}) {
      const { data } = await axios({ url, params, responseType: 'blob' });
      return data;
    },
    // 执行下载动作
    async downloadFile({ url, name }, params) {
      const blob = await this.downLoadByUrl(url, params);
      const fileName = !name ? url.slice(url.lastIndexOf('/') + 1) : name;
      // ie10+
      if (navigator.msSaveBlob) {
        navigator.msSaveBlob(blob, decodeURI(fileName));
      } else {
        const downloadUrl = window.URL.createObjectURL(blob);
        let a = document.createElement('a');
        a.href = downloadUrl;
        a.download = decodeURI(fileName);
        a.click();
        a = null;
      }
    }
  }
};
</script>

<style lang="less" scoped>
.upload-wrap {
  width: 100%;
  float: left;
  & > div {
    display: inline-block;
  }
  /deep/ .el-upload--picture-card {
    display: flex;
    width: 100% !important;
    height: 148px;
    background: none;
    border: none;
    border-radius: 0;
    pointer-events: none;
    .el-upload-list__item {
      position: relative;
      width: 148px;
      height: 100%;
      line-height: inherit;
      margin: 0 10px 0 0;
      border-radius: 6px;
      background-color: #fff;
      border: 1px dashed #c0ccda;
      pointer-events: auto !important;
      overflow: hidden;
      .title {
        position: absolute;
        width: 100%;
        height: 20px;
        background-color: rgba(0, 0, 0, 0.6);
        left: 0;
        right: 0;
        bottom: 0;
        line-height: 20px;
        color: #fff;
        font-size: 12px;
      }
      .img {
        display: block;
        max-width: 100%;
      }
      .img_isEdit {
        max-height: 58px;
        max-width: 140px;
        vertical-align: middle;
        margin: 0 auto;
      }
      .el-upload-list__item-actions {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        top: 0;
        z-index: 1;
        cursor: default;
        opacity: 0;
        background-color: rgba(0, 0, 0, 0.5);
        transition: all 0.3s;
        &:hover {
          opacity: 1;
        }
        .el-upload-list__item-dot {
          display: inline-block;
          width: 20px;
          padding: 0 10px;
          cursor: pointer;
          i {
            font-size: 20px;
            color: #fff;
          }
        }
      }
    }
    .upload-icon-plus {
      i {
        vertical-align: middle;
      }
    }
  }
  .el-upload__tip {
    line-height: 20px;
  }
}

.btn_yl {
  position: relative;
  .btn_bottom {
    display: none;
  }
  &:hover {
    .btn_ys {
      cursor: pointer;
      color: #fff;
      margin-right: 20px;
    }
    .btn_bottom {
      display: block;
      text-align: center;
      width: 100%;
      height: 50px;
      line-height: 50px;
      position: absolute;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.5);
    }
  }
}
.preview-wrap {
  display: flex;
  justify-content: center;
  img {
    display: block;
    max-width: 100%;
  }
}
</style>
